/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package de.bht.esa.weather.resourcemanager;

import de.bht.esa.weather.model.WetterComForecast;
import de.bht.esa.weather.model.WetterComLocation;
import de.bht.esa.weather.remotebeaninterfaces.WeatherAggregationRequest;
import de.bht.esa.weather.remotebeaninterfaces.WeatherAggregationResponse;
import de.bht.esa.weather.remotebeaninterfaces.WeatherResourceManager;
import de.bht.esa.weather.weathersoapclient.Exception_Exception;
import de.bht.esa.weather.weathersoapclient.WeatherService;
import de.bht.esa.weather.weathersoapclient.WeatherService_Service;
import java.util.List;
import java.util.UUID;
import javax.ejb.EJBException;
import javax.ejb.Remote;
import javax.ejb.Stateless;
import javax.jms.JMSException;
import javax.jms.MessageProducer;
import javax.jms.ObjectMessage;
import javax.jms.Queue;
import javax.jms.QueueConnection;
import javax.jms.QueueConnectionFactory;
import javax.jms.QueueSession;
import javax.naming.InitialContext;
import javax.naming.NamingException;

/**
 * Zentrale Klasse, welche alle Anfragen nach Orten beziehnungsweise Wetterberichten steuert.
 * 
 * @author Andreas Goese
 * @Sven Höche
 */
@Stateless
@Remote(WeatherResourceManager.class)
public class WeatherResourceManagerImpl implements WeatherResourceManager{
    
    /**
     * Funktion für die Suche nach Orten. Siehe Weather-Soap-Client
     * 
     * @param query Suchstring für die Suche nach Orten (z.B. "Berlin", oder "13353")
     * @return Wenn Orte gefunden wurden kommt ein Array zurück, welches die gefundenen Orte enthält.
     */
    @Override
    public WetterComLocation[] searchLocation(String query){
        WeatherService_Service proxy = new WeatherService_Service();
        WeatherService service = proxy.getWeatherServicePort();
        try {
            List<de.bht.esa.weather.weathersoapclient.WetterComLocation> resultList = service.getLocations(query);
            return this.convertToNativeArray(resultList);
        } catch (Exception_Exception ex) {
            throw new EJBException(ex);
        }
    }
    
    /**
     * Gibt den aggregierten Wetterbericht zurück, sofern dieser schon fertiggestellt wurde. Wenn der Wetterbericht noch nicht fertiggestellt wurde,
     * wird eine IllegalStateException geworfen. Ebenso, wenn bei der Aggregation ein Fehler aufgetreten ist.
     * 
     * @param resourceId Die Resource-Id, die bei der Wetter-Anfrage generiert wurde
     * @return Sofern das Wetter fertig aggregiert wurde, wird das Wetter zurückgegeben.
     */
    @Override
    public WetterComForecast getForecast(String resourceId){
        try{
            InitialContext ctx = new InitialContext();
            WeatherResourceEntityManager rm = (WeatherResourceEntityManager) ctx.lookup("java:global/de.bht.esa.weather_WeatherResourceManager_war_1.0-SNAPSHOT/WeatherResourceEntityManager");
            WeatherResourceEntity ressourceEntity = rm.getWeatherResourceEntity(resourceId);

            if (ressourceEntity == null) {
                throw new EJBException(new IllegalStateException("Resource not found"));
            }

            if(ressourceEntity.getState() == -1){
                throw new EJBException(new IllegalStateException("Aggregation failed"));
            }
            
            return rm.getWeatherResourceEntity(resourceId).getForecast();
        }catch(NamingException ex){
            throw new EJBException(ex);
        }
    }

    /**
     * Fragt einen Wetterbericht an.
     * 
     * 
     * @param request Enthält entweder Koordinaten, oder aber Einen Suchstring für den gewünschten Ort
     * @return Gibt die temporäre Ressource-ID zurück, mit der anschließend das aggregierte Wetter gepollt werden kann.
     */
    @Override
    public String requestWeatherForecast(WeatherAggregationRequest request){
        try{
            InitialContext ctx = new InitialContext();
            WeatherResourceEntityManager rm = (WeatherResourceEntityManager) ctx.lookup("java:global/de.bht.esa.weather_WeatherResourceManager_war_1.0-SNAPSHOT/WeatherResourceEntityManager");

            request.setResourceId(UUID.randomUUID().toString());
            this.sendRequest(request);

            WeatherResourceEntity tmp = new WeatherResourceEntity(request.getResourceId());
            rm.saveWeatherResourceEntity(tmp);

            return request.getResourceId();
        }catch(Exception ex){
            throw new EJBException(ex);
        }
    }
    
    /**
     * Konvertiert die vom SoapClient zurückgegebenen Orte in Orts-Objekte der nativen WetterComLocation-Klasse.
     * 
     * @param list Ein Array mit - vom SoapClient zurückgebebenen - Orts-Objekten. Die Klasse wurde generiert und muss daher in die Native Klasse konvertiert werden.
     * @return Konvertierte Orts-Liste
     */
    private WetterComLocation[] convertToNativeArray(List<de.bht.esa.weather.weathersoapclient.WetterComLocation> list){
        WetterComLocation[] resultArray = new WetterComLocation[list.size()];
        for(int i = 0; i < list.size(); i++){
            resultArray[i] = this.convertToNativeModel(list.get(i));
        }
        return resultArray;
    }
    
    /**
     * Siehe convertToNativeArray(...)
     * 
     * @param tmp Siehe convertToNativeArray(...)
     * @return Siehe convertToNativeArray(...)
     */
    private WetterComLocation convertToNativeModel(de.bht.esa.weather.weathersoapclient.WetterComLocation tmp){
        WetterComLocation res = new WetterComLocation();
        res.setAddress(tmp.getAddress());
        res.setCityCode(tmp.getCityCode());
        res.setCounty(tmp.getCounty());
        res.setCountry(tmp.getCountry());
        res.setDistrict(tmp.getDistrict());
        res.setName(tmp.getName());
        res.setState(tmp.getState());
        res.setZip(tmp.getZip());
        return res;
    }


    /**
     * Speichert den fertig aggregierten Wetterbericht in die Datenbank
     * 
     * @param response Der Response, der sowohl den Wetterbericht, also auch die Location enthält.
     */
    @Override
    public void saveWeatherAggregationResponse(WeatherAggregationResponse response){
        try{
            InitialContext ctx = new InitialContext();
            WeatherResourceEntityManager rm = (WeatherResourceEntityManager) ctx.lookup("java:global/de.bht.esa.weather_WeatherResourceManager_war_1.0-SNAPSHOT/WeatherResourceEntityManager");
            
            WeatherResourceEntity tmp = rm.getWeatherResourceEntity(response.getRequest().getResourceId());
            if(response.getError() == null){
                tmp.setForecast(response.getForecast());
                tmp.setState(1); 
            }else{
                tmp.setState(-1);
            }
            rm.saveWeatherResourceEntity(tmp);
        }catch(Exception ex){
            throw new EJBException(ex);
        }
    }

    /**
     * Legt den Aggregations-Request in die Queue zur abarbeitung durch den WeatherResourceAggrgator.
     * 
     * @param request Enthält entweder Koordinaten, oder aber Einen Suchstring für den gewünschten Ort (siehe requestWeatherForecast(...))
     */
    protected void sendRequest(WeatherAggregationRequest request) throws NamingException,JMSException{
        final InitialContext context = new InitialContext();
        final QueueConnectionFactory queueFactory = (QueueConnectionFactory) context.lookup("WeatherQueueFactory");
        final Queue jmsQueue = (Queue)context.lookup("WeatherQueue");
        context.close();
        
        final QueueConnection queueConnection = queueFactory.createQueueConnection();
        final QueueSession queueSession = queueConnection.createQueueSession(false, QueueSession.AUTO_ACKNOWLEDGE);
        final MessageProducer producer = queueSession.createProducer(jmsQueue);
        final ObjectMessage msg = queueSession.createObjectMessage(request);
        producer.send(msg);
        producer.close();
        queueSession.close();
        queueConnection.close();
    }

}
